<?php

namespace app\common\service;

use app\common\job\TransferBatchRetry;
use app\common\model\AccountBill;
use app\common\model\Transfer;
use app\common\model\TransferBatch;
use app\index\dao\TransferBatchDao;
use app\index\dao\TransferDao;
use app\index\traits\PaymentTrait;
use ShidianUtil\Concurrent\Synchronizer;
use ShidianUtil\Utils\TimeUtil;
use think\facade\Config;
use think\facade\Log;
use think\facade\Queue;

/**
 * 转账
 * Class TransfersServer
 * @package app\common\service
 */
class TransfersServer
{
    use PaymentTrait;

    private Synchronizer $synchronizer;

    public function __construct(Synchronizer $synchronizer)
    {
        $this->synchronizer = $synchronizer;
    }

    /**
     * 添加对人转账申请（根据转账支付方式，触发转账）
     * @param string $transferType 转账业务类型
     * @param $memberId
     * @param string $payMethod 转账账户类型
     * @param $accountNumber
     * @param $totalMoney
     * @param $describe
     * @param array $append
     * @param array $attach
     * @return bool
     */
    public function pushTransferToMember($transferType, $memberId, $payMethod, $accountNumber, $totalMoney, $describe, array $append, array $attach = []): bool
    {
        $memberType = 'member';

        // 插入转账申请
        $record = $this->getTransferDao()->insertTransfer(0, $transferType, $memberType, $memberId, $payMethod, $accountNumber, $totalMoney, $describe, $append, $attach);
        if (is_null($record)) {
            return false;
        }

        return $this->pushTransfer($record);
    }

    /**
     * 添加对公司转账申请（根据转账支付方式，触发转账）
     * @param string $transferType 转账业务类型
     * @param $companyId
     * @param string $payMethod 转账账户类型
     * @param $accountNumber
     * @param $totalMoney
     * @param $describe
     * @param array $append
     * @param array $attach
     * @return bool
     */
    public function pushTransferToCompany($transferType, $companyId, $payMethod, $accountNumber, $totalMoney, $describe, array $append, array $attach = []): bool
    {
        $memberType = 'company';

        // 插入转账申请
        $record = $this->getTransferDao()->insertTransfer(0, $transferType, $memberType, $companyId, $payMethod, $accountNumber, $totalMoney, $describe, $append, $attach);
        if (is_null($record)) {
            return false;
        }

        return $this->pushTransfer($record);
    }

    /**
     * 添加对人反向转账申请（根据转账支付方式，触发转账）
     * @param string $transferType 转账业务类型
     * @param $memberId
     * @param string $payMethod 转账账户类型
     * @param $accountNumber
     * @param $totalMoney
     * @param $describe
     * @param array $append
     * @param array $attach
     * @return bool
     */
    public function pushTransferFromMember($transferType, $memberId, $payMethod, $accountNumber, $totalMoney, $describe, array $append, array $attach = []): bool
    {
        $memberType = AccountBill::MEMBER;

        // 插入转账申请
        $record = $this->getTransferDao()->insertTransfer(1, $transferType, $memberType, $memberId, $payMethod, $accountNumber, $totalMoney, $describe, $append, $attach);
        if (is_null($record)) {
            return false;
        }

        return $this->pushTransfer($record);
    }

    /**
     * 添加对公司转账申请（根据转账支付方式，触发转账）
     * @param string $transferType 转账业务类型
     * @param $companyId
     * @param string $payMethod 转账账户类型
     * @param $accountNumber
     * @param $totalMoney
     * @param $describe
     * @param array $append
     * @param array $attach
     * @return bool
     */
    public function pushTransferFromCompany($transferType, $companyId, $payMethod, $accountNumber, $totalMoney, $describe, array $append, array $attach = []): bool
    {
        $memberType = 'company';

        // 插入转账申请
        $record = $this->getTransferDao()->insertTransfer(1, $transferType, $memberType, $companyId, $payMethod, $accountNumber, $totalMoney, $describe, $append, $attach);
        if (is_null($record)) {
            return false;
        }

        return $this->pushTransfer($record);
    }

    /**
     * 查询转账结果
     * @param $id
     * @return bool|null
     */
    public function resultQuery($id): ?bool
    {

        $transfer = Transfer::where('id', $id)->find();
        if (!$transfer) {
            return false;
        }

        // 已经有结果
        if ($transfer->transferStatus != 1) {
            if ($transfer->transferStatus == 2) {
                return true;
            }
            return false;
        }

        $paymentsConfig = Config::get('payment', []);

        // 根据不同支付方式，就调用不同的逻辑
        $payMethod = $transfer->payMethod;
        if (isset($paymentsConfig['transfers'][$payMethod])) {
            // 单笔转账同步到账接口

            // 同步到账不需要查询

            return true;

        } elseif (isset($paymentsConfig['transfersbatch'][$payMethod])) {
            // 批量转账异步到账接口

            return $this->getTransferBatch($payMethod)->transferResultQuery($transfer);

        } else {

            Log::error('查询转账结果出错，错误的转账支付方式,ID=' . $transfer->id . ' payMethod=' . $payMethod);

            return false;
        }
    }

    /**
     * 立即处理某支付方式的批量转账
     * @param $payMethod
     * @return void
     */
    public function batchTransfers($payMethod): void
    {
        $now = time();
        $today = TimeUtil::stamp(TimeUtil::DAY, $now);
        $limit = Config::get('payment.transfersbatch.' . $payMethod . '.max', 1);
        $dayMoney = Config::get('payment.transfersbatch.' . $payMethod . '.dayMoney', 100);

        // 查询该支付方式的待转账记录（每个人只能有一个）
        $collection = Transfer::where('transferStatus', 0)
            ->where('transferBatchId', 0)
            ->where('payMethod', $payMethod)
            ->order('id', 'DESC')
            ->limit($limit)
            ->select();

        // 每天提现限额
        $collection = $collection->filter(function (Transfer $transfer) use ($today, $dayMoney, $payMethod) {

            // 查询 提现中金额 + 今天提现成功金额
            $totalMoney = Transfer::where('memberId', $transfer->memberId)
                ->where('memberType', $transfer->memberType)
                ->where(function ($query) use ($today) {
                    $query->whereIn('transferStatus', [0, 1], 'OR')
                        ->whereOr('transferTime', '>=', $today);
                })->where('payMethod', $payMethod)
                ->where('id', '<>', $transfer->id)
                ->sum('totalMoney');
            if (is_null($totalMoney)) {
                $totalMoney = 0;
            }

            // 今天预计提现总金额
            $todayMoney = $transfer->totalMoney + $totalMoney;

            // 每天提现不能超过限额
            if ($todayMoney > $dayMoney) {
                Log::warning('提现超过限额' . $todayMoney . '>' . $dayMoney);
                return false;
            }

            return true;
        })->values();

        if ($collection->isEmpty()) {
            return;
        }

        // 插入批量转账记录
        $orderNo = date('YmdHis', $now);
        $totalMoney = array_sum($collection->column('totalMoney'));
        $totalNum = $collection->count();
        $params = [
            'payMethod' => $payMethod,
            'orderNo' => $orderNo,
            'tradeNid' => '',
            'describe' => '批量提现转账',
            'totalMoney' => $totalMoney,
            'totalNum' => $totalNum,
            'successNum' => 0,
        ];
        $record = $this->getTransferBatchDao()->insertActive($params);
        if (is_null($record)) {
            Log::error('插入批量转账记录失败');
            return;
        }

        // 保存批量转账单号
        $ids = $collection->column('id');
        Transfer::whereIn('id', $ids)->update([
            'transferBatchId' => $record->id,
            'transferBatchOrderNo' => $record->orderNo,
        ]);

        // 发起批量转账
        $result = $this->getTransferBatch($payMethod)->transferBatch($record, $collection);

        if (!$result) {
            // 半小时后重试,
            $job_data = ['id' => $record->id];
            Queue::later(1800, TransferBatchRetry::class, $job_data);
        }
    }

    /**
     * 重试某个批量转账
     * @param $id
     * @return bool
     */
    public function retryBatchTransfer($id): bool
    {
        $record = TransferBatch::where('id', $id)->find();
        $payMethod = $record->payMethod;
        $collection = Transfer::where('transferBatchId', $id)
            ->whereIn('transferStatus', [0, 3])
            ->order('id', 'DESC')
            ->select();

        // 发起批量转账
        return $this->getTransferBatch($payMethod)->transferBatch($record, $collection);
    }


    /**
     * @param Transfer $record
     * @return bool
     */
    private function pushTransfer(Transfer $record): bool
    {
        $payMethod = $record->payMethod;

        $paymentsConfig = Config::get('payment', []);

        // 根据不同支付方式，就调用不同的逻辑
        if (isset($paymentsConfig['transfers'][$payMethod])) {
            // 单笔转账同步到账接口

            return $this->getTransfer($payMethod)->transfer($record);

        } elseif (isset($paymentsConfig['transfersbatch'][$payMethod])) {
            // 批量转账异步到账接口

            // 批量转账通过 定时器触发，不需要 手动触发

            return true;
        } else {

            Log::error('添加转账申请出错，错误的转账支付方式,ID=' . $record->id . ' payMethod=' . $payMethod);

            return false;
        }

        return true;
    }

    private function getTransferDao(): TransferDao
    {
        return app(TransferDao::class);
    }

    private function getTransferBatchDao(): TransferBatchDao
    {
        return app(TransferBatchDao::class);
    }
}
